package com.starsoft.quant.web;

import java.text.DecimalFormat;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import javax.servlet.ServletRequest;

import org.springframework.beans.factory.annotation.Autowired;

import com.starsoft.frame.base.bean.BaseAcount;
import com.starsoft.frame.base.service.AcountService;
import com.starsoft.frame.base.service.UserService;
import com.starsoft.frame.core.web.WebUtil;
import com.starsoft.frame.util.DateUtil;
import com.starsoft.frame.util.MapUtil;
import com.starsoft.frame.util.StringUtil;
import com.starsoft.quant.bean.QuantAcount;
import com.starsoft.quant.bean.QuantModel;
import com.starsoft.quant.bean.QuantSummary;
import com.starsoft.quant.executor.StrategyReport;
import com.starsoft.quant.service.ModelParamsService;
import com.starsoft.quant.service.QuantAcountService;
import com.starsoft.quant.service.QuantReportService;
import com.starsoft.smdc.bean.SmdcMarketDaily;
import com.starsoft.smdc.bean.SmdcSecurity;
import com.starsoft.smdc.service.HolidayService;
import com.starsoft.smdc.service.MarketDailyService;
import com.starsoft.smdc.util.SmdcConst;
import com.starsoft.trade.bean.TradeBill;
import com.starsoft.trade.bean.TradeSummary;
import com.starsoft.trade.service.AcountStatusService;
import com.starsoft.trade.service.TradeAcountService;
import com.starsoft.trade.service.TradeBillService;
import com.starsoft.trade.service.TradeSummaryService;

public class QuantReportControllerBase {

	@Autowired
	protected AcountService baseAcountService;
	
	@Autowired
	protected QuantAcountService quantAcountService;

	@Autowired
	protected ModelParamsService modelParamService;

	@Autowired
	protected QuantReportService reportService;

	@Autowired
	protected HolidayService holidayService;

	@Autowired
	protected AcountStatusService acountStatusService;

	@Autowired
	protected TradeSummaryService tradeSummaryService;

	@Autowired
	protected TradeAcountService tradeAcountService;

	@Autowired
	protected MarketDailyService marketService;
	
	@Autowired
	protected TradeBillService billService;

	protected List<QuantAcount> getQuantAcount(ServletRequest request, List<String> message) {
		String  acountIdsStr = request.getParameter("acountIds");
		if(StringUtil.isEmpty(acountIdsStr)){
			acountIdsStr= String.valueOf(UserService.getUserContext().getAcountId());
		}
		String[] acountIds = acountIdsStr.split(",");
		List<QuantAcount> acounts = new ArrayList<QuantAcount>();
		for (String acountIdStr : acountIds) {
			Integer acountId = Integer.parseInt(acountIdStr);
			QuantAcount quantAcount = quantAcountService.getAcount(acountId);
			QuantModel quantModel = quantAcountService.getModel(acountId);
			if (quantAcount == null || quantModel == null) {
				message.add("acountId：" + acountIdStr + ", 未配置策略");
			} else {
				acounts.add(quantAcount);
			}
		}
		return acounts;
	}

	protected Map<String, Object> loadConfig(QuantAcount quantAcount, Date reportDate) {
		BaseAcount baseAcount = baseAcountService.getAcount(quantAcount.getAcountId());
		QuantModel quantModel = quantAcountService.getModel(quantAcount.getAcountId());
		Map<String, Object> config = new HashMap<String, Object>();
		config.put("acountId", baseAcount.getAcountId());
		config.put("acountName", baseAcount.getAcountName());
		config.put("modelCode", quantModel.getCode());
		config.put("modelName", quantModel.getName());
		config.put("startDate", quantAcount.getStartDate());
		config.put("reportDateX", reportDate);
		String secList = quantAcountService.getSecList(quantAcount.getAcountId());
		config.put("secList", secList);
		config.putAll(modelParamService.getAcountModelParamsMap(quantAcount));
		return config;
	}

	protected Map<String, Object> loadRisk(Integer acountId, Date reportDate) {
		BaseAcount baseAcount = baseAcountService.getAcount(acountId);
		StrategyReport report = reportService.getReport(acountId, reportDate);
		QuantSummary summary = report.getSummary();
		if (summary == null || summary.getAlpha() == null) {
			summary = reportService.updateAnalysis(acountId, reportDate);
		}
		Map<String, Object> result = MapUtil.toMap(summary);
		result.put("excessProfit", summary.getProfit() - summary.getBaseProfit());
		result.put("acountId", baseAcount.getAcountId());
		result.put("acountName", baseAcount.getAcountName());
		return result;
	}
	
	private static class BillMarkPoint {
		Date date;
		List<TradeBill> bills = new ArrayList<TradeBill>();
		double value;
		public Map<String, Object> toMap() {
			DecimalFormat df = new DecimalFormat("#.00");
			Map<String, Object> result = new HashMap<>();
			result.put("value", df.format(value));
			result.put("coord", new Object[]{DateUtil.toString(date), df.format(value)});
			result.put("tooltip", DateUtil.toString(date)+"<br>交易"+bills.size()+"笔");
			return result;
		}
	}

	protected Map<String, Object> loadProfit(Integer acountId, Date beginDate, Date reportDate) {
		Map<String, Object> result = new HashMap<String, Object>();
		acountStatusService.updateAndGet(acountId, reportDate);
		SmdcSecurity baseIndex = tradeAcountService.getBaseIndex(acountId);
		BaseAcount baseAcount = baseAcountService.getAcount(acountId);
		
		List<TradeBill> bills = billService.getBills(acountId, beginDate, reportDate);
		Map<Date, BillMarkPoint> billMap = new HashMap<>();
		for(TradeBill bill : bills){
			Date billDate = DateUtil.floorTime(bill.getBillTime());
			BillMarkPoint billMarkPoint = billMap.get(billDate);
			if(billMarkPoint == null){
				billMarkPoint = new BillMarkPoint();
				billMarkPoint.date = billDate;
				billMap.put(billDate, billMarkPoint);
			}
			billMarkPoint.bills.add(bill);
		}
		
		Date adjBeginDate = holidayService.getNextBusinessDay(beginDate, -1);
		List<TradeSummary> summaryList = tradeSummaryService.getAcountBetween(acountId, adjBeginDate, reportDate);
		TradeSummary summaryBase = summaryList.get(0);

		List<Date> timeLine = new ArrayList<>();
		List<Double> baseProfit = new ArrayList<>();
		List<Double> profit = new ArrayList<>();
		for (int i = 0; i < summaryList.size(); i++) {
			TradeSummary summary = summaryList.get(i);
			Date summaryDate = summary.getId().getAsofdate();
			timeLine.add(summaryDate);
			baseProfit.add((summary.getBaseProfit() - summaryBase.getBaseProfit()) / (summaryBase.getBaseProfit() + 1)
					* 100);
			double profitValue = (summary.getProfit() - summaryBase.getProfit()) / (summaryBase.getProfit() + 1) * 100;
			profit.add(profitValue);
			BillMarkPoint billMarkPoint = billMap.get(summaryDate);
			if(billMarkPoint != null){
				billMarkPoint.value = profitValue;
			}
		}

		Map<String, Object> lineData = new HashMap<String, Object>();
		lineData.put(baseAcount.getAcountId()+"_"+baseAcount.getAcountName(), profit);
		lineData.put(baseIndex.getSecName(), baseProfit);
		Map<String, Object> requestData = new HashMap<String, Object>();
		requestData.put("beginDate", beginDate);

		List<Map<String, Object>> markPointData = new ArrayList<Map<String,Object>>();
		for(BillMarkPoint billMarkPint : billMap.values()){
			markPointData.add(billMarkPint.toMap());
		}
		
		result.put("timeLine", timeLine);
		result.put("requestData", requestData);
		result.put("lineData", lineData);
		result.put("markPointData", markPointData);
		
		return result;
	}

	protected Map<String, Object> loadProfit(List<QuantAcount> acounts, Date beginDate, Date reportDate) {
		Map<String, Object> result = new HashMap<>();
		ProfitBean profitBean = new ProfitBean();
		for (QuantAcount quantAcount : acounts) {
			Integer acountId = quantAcount.getAcountId();
			Date acountReportDate = reportDate;
			Date acountBeginDate = beginDate;
			if (quantAcount.getEndDate() != null && acountReportDate.after(quantAcount.getEndDate())) {
				acountReportDate = quantAcount.getEndDate();
			}
			if (beginDate == null || beginDate.before(quantAcount.getStartDate())) {
				acountBeginDate = quantAcount.getStartDate();
			}
			acountBeginDate = tradeSummaryService.getLastSummaryDate(acountId, acountBeginDate);

			acountStatusService.updateAndGet(acountId, acountReportDate);
			BaseAcount baseAcount = baseAcountService.getAcount(acountId);
			String lineName = baseAcount.getAcountName() + "_" + baseAcount.getAcountId();
			List<TradeSummary> summaryList = tradeSummaryService.getAcountBetween(acountId, acountBeginDate, acountReportDate);
			if(summaryList.isEmpty()){
				continue;
			}
			TradeSummary summaryBase = summaryList.get(0);

			for (int i = 0; i < summaryList.size(); i++) {
				TradeSummary summary = summaryList.get(i);
				Double profitNum = (summary.getProfit() - summaryBase.getProfit()) / (summaryBase.getProfit() + 1) * 100;
				profitBean.addSummary(lineName, summary.getId().getAsofdate(), profitNum);
			}
		}

		for (QuantAcount quantAcount : acounts) {
			SmdcSecurity baseIndex = tradeAcountService.getBaseIndex(quantAcount.getAcountId());
			List<SmdcMarketDaily> markets = marketService.getBetweenAnd(baseIndex.getSecId(), profitBean.getStartDate(),
					profitBean.getEndDate());
			double baseValue = markets.get(0).getClosePrice();
			for (SmdcMarketDaily market : markets) {
				Double profit = market.getClosePrice() / baseValue * 100 - 100;
				profitBean.addBaseSummary(baseIndex.getSecName(), market.getId().getTradeDate(), profit);
			}
		}
		Map<String, Object> requestData = new HashMap<String, Object>();
		requestData.put("beginDate", beginDate);
		
		result.put("timeLine", profitBean.timeLine);
		result.put("lineData", profitBean.getAdjLineDatas());
		result.put("requestData", requestData);
		return result;
	}
	
	class ProfitBean{
		TreeSet<Date> timeLine = new TreeSet<Date>();
		Map<String, Map<Date, Double>> lineDatas = new LinkedHashMap<>();
		
		public Date getStartDate(){
			return timeLine.first();
		}
		
		public Date getEndDate(){
			return timeLine.last();
		}
		
		public void addSummary(String lineName, Date date, Double profit) {
			Map<Date, Double> lineDate = lineDatas.get(lineName);
			if (lineDate == null) {
				lineDate = new HashMap<>();
				lineDatas.put(lineName, lineDate);
			}
			lineDate.put(date, profit);
			timeLine.add(date);
		}
		
		public void addBaseSummary(String lineName, Date date, Double profit) {
			Map<Date, Double> lineDate = lineDatas.get(lineName);
			if (lineDate == null) {
				lineDate = new HashMap<>();
				lineDatas.put(lineName, lineDate);
			}
			if (timeLine.contains(date)) {
				lineDate.put(date, profit);
			}
		}
		
		public Map<String, List<Double>> getAdjLineDatas() {
			Map<String, List<Double>> adjLineDates = new HashMap<>();
			lineDatas.forEach((lineName, lineDataMap) -> {
				List<Double> lineDate = new ArrayList<>(timeLine.size());
				adjLineDates.put(lineName, lineDate);
				timeLine.forEach(date -> {
					if (lineDataMap.containsKey(date)) {
						lineDate.add(lineDataMap.get(date));
					} else {
						lineDate.add(null);
					}
				});
			});
			return adjLineDates;
		}
		
	}

	protected Date getBeginDate(ServletRequest request, Date reportDate, List<QuantAcount> acounts) {
		String beginDateType = request.getParameter("beginDateType");
		Date beginDate = WebUtil.getDate(request, "beginDate", "yyyy-MM-dd");
		if(StringUtil.isEmpty(beginDateType)){
			return beginDate;
		}

		Date actualReportDate = SmdcConst.SMDC_BEGIN_DATE;
		for (QuantAcount acount : acounts) {
			if(actualReportDate == null){
			} else if (acount.getEndDate() == null) {
				actualReportDate = null;
			} else if(actualReportDate.before(acount.getEndDate())){
				actualReportDate = acount.getEndDate();
			}
		}
		if(actualReportDate == null || actualReportDate.after(reportDate)){
			actualReportDate = reportDate;
		}
	
		ZonedDateTime now = DateUtil.toZoned(actualReportDate);
		if ("thisMonth".equalsIgnoreCase(beginDateType)) {
			beginDate = Date.from(now.withDayOfMonth(1).toInstant());
		} else if ("threeMonth".equalsIgnoreCase(beginDateType)) {
			beginDate =  Date.from(now.withDayOfMonth(1).plusMonths(-2).toInstant());
		} else if ("thisYear".equalsIgnoreCase(beginDateType)) {
			beginDate =  Date.from(now.withDayOfYear(1).toInstant());
		} else if ("all".equalsIgnoreCase(beginDateType)) {
			beginDate = null;
		} else {
			beginDate = null;
		}

		return beginDate;
	}
	
	public Date getReportDate(ServletRequest request){
		Date reportDate = WebUtil.getDate(request, "reportDate", "yyyy-MM-dd");
		if (reportDate == null) {
			reportDate = new Date();
		}
		boolean isBusy = holidayService.isBusinessDay(reportDate);
		if (DateUtil.isToday(reportDate)){
			boolean isBeforeOpen = holidayService.isBeforeOpen(new Date());
			if(!isBusy || isBeforeOpen) {
				reportDate = holidayService.getNextBusinessDay(reportDate, -1);
			}
		} else if(!isBusy){
			reportDate = holidayService.getNextBusinessDay(reportDate, -1);
		}
		return reportDate;
	}
}
